package cn.jsj.invite.service.impl;

import cn.jsj.invite.dao.InvitationDao;
import cn.jsj.invite.dao.StudentDao;
import cn.jsj.invite.pojo.InvitationInfo;
import cn.jsj.invite.pojo.StudentEnt;
import cn.jsj.invite.service.RecommendationService;
import cn.jsj.invite.utils.Pair;
import org.apache.commons.collections4.SetUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class RecommendationServiceImpl implements RecommendationService {
    private final StudentDao studentDao;

    private final InvitationDao invitationDao;

    public RecommendationServiceImpl(StudentDao studentDao, InvitationDao invitationDao) {
        this.studentDao = studentDao;
        this.invitationDao = invitationDao;
    }

    @Override
    public List<InvitationInfo> studentFindWork(Integer studentId) {
        //查找学生信息
        StudentEnt studentEnt = studentDao.selectOneStudentById(studentId);
        //如果不存在就返回null
        if (studentEnt == null) {
            return null;
        }
        //参与对比的学生的标签
        String tagList = studentEnt.getTagList();
        //获取近30天的帖子
        List<InvitationInfo> invitationInfos = invitationDao.selectInvitationRecentOneMonth();
        List<Pair> score = new ArrayList<>();
        for (InvitationInfo info : invitationInfos) {
            score.add(new Pair(info, calculationSimilarity(info.getTagList(), tagList)));
        }
        invitationInfos.clear();
        List<Pair> collect =
                score.stream().sorted(Comparator.comparing(Pair::getaFloat).reversed()).limit(5).collect(Collectors.toList());
        ArrayList<InvitationInfo> arrayList = new ArrayList<InvitationInfo>() {{
            for (Pair pair : collect) {
                add(pair.getInfo());
            }
        }};
        InvitationServiceImpl.invitationInfoDataFormat(arrayList);
        return arrayList;
    }


    /**
     * 余弦相似度
     */
    private Float calculationSimilarity(String a, String b) {
        if (a == null || b == null) {
            return 0F;
        }

        Set<Integer> aChar = stringToIntegerSet(a);
        Set<Integer> bChar = stringToIntegerSet(b);

        // 统计字频
        Map<Integer, Integer> aMap = new HashMap<>();
        Map<Integer, Integer> bMap = new HashMap<>();
        for (Integer a1 : aChar) {
            aMap.put(a1, aMap.getOrDefault(a1, 0) + 1);
        }
        for (Integer b1 : bChar) {
            bMap.put(b1, bMap.getOrDefault(b1, 0) + 1);
        }

        // 向量化
        Set<Integer> union = SetUtils.union(aChar, bChar);
        int[] aVec = new int[union.size()];
        int[] bVec = new int[union.size()];
        List<Integer> collect = new ArrayList<>(union);
        for (int i = 0; i < collect.size(); i++) {
            aVec[i] = aMap.getOrDefault(collect.get(i), 0);
            bVec[i] = bMap.getOrDefault(collect.get(i), 0);
        }

        // 分别计算三个参数
        int p1 = 0;
        for (int i = 0; i < aVec.length; i++) {
            p1 += (aVec[i] * bVec[i]);
        }

        float p2 = 0f;
        for (int i : aVec) {
            p2 += (i * i);
        }
        p2 = (float) Math.sqrt(p2);

        float p3 = 0f;
        for (int i : bVec) {
            p3 += (i * i);
        }
        p3 = (float) Math.sqrt(p3);

        return ((float) p1) / (p2 * p3);
    }

    /**
     * String -> Set<Integer>
     * java9+ 的写法
     * return a.chars().boxed().collect(Collectors.toSet());
     */
    private Set<Integer> stringToIntegerSet(String s) {
        IntStream.Builder aBuilder = IntStream.builder();
        //add(int)方法底层调用accept方法
        for (char aChar : s.toCharArray()) {
            aBuilder.accept(aChar);
        }
        return aBuilder.build().boxed().collect(Collectors.toSet());
    }


}
